from flask import Flask, request, jsonify, render_template
import os
import requests
import configparser
from util import parse_item_list
import json

app = Flask(__name__)

def decode_request_data(raw_data):
    # 尝试不同的编码方式
    encodings = ['utf-8', 'gbk', 'gb2312', 'iso-8859-1']
    data = None
    
    for encoding in encodings:
        try:
            if isinstance(raw_data, bytes):
                decoded_data = raw_data.decode(encoding)
            else:
                decoded_data = raw_data
            data = json.loads(decoded_data)
            print(f"Successfully decoded with {encoding}")
            break
        except Exception as e:
            print(f"Failed to decode with {encoding}: {str(e)}")
            continue
            
    return data

@app.route('/ask', methods=['POST'])
def ask_question():
    """Handle user questions via POST request
    
    Example curl command:
        curl -X POST http://localhost:7777/ask \
        -H "Content-Type: application/json" \
        -d '{"question": "What is the capital of France?"}'
    
    Args:
        question (str): The user's question passed in the request body
        
    Returns:
        JSON response with a greeting message
        
    Raises:
        400 Bad Request: If question parameter is missing or empty
    """
    try:
        # Get raw data and decode with UTF-8
        raw_data = request.get_data()
        raw_data = decode_request_data(raw_data)
        if not raw_data:
            return jsonify({"error": "Request body is empty"}), 400
            
        question = raw_data['question'].strip()
        if not question:
            return jsonify({"error": "Question cannot be empty"}), 400
            
    except UnicodeDecodeError as e:
        return jsonify({"error": f"Invalid character encoding: {str(e)}"}), 400
    except Exception as e:
        return jsonify({"error": f"Invalid request format: {str(e)}"}), 400
        
    try:
        # Generate prompt from the question
        prompt = generate_prompt(question)
        resp = call_deepseek(prompt)
        # Parse the response using parse_item_list if it contains ITEM_LIST
        try:
            parsed_items = parse_item_list(resp)
            response = jsonify({
                "parsed_items": parsed_items
            })
            response.headers['Content-Type'] = 'application/json; charset=utf-8'
            return response
        except ValueError:
            # If parsing fails, return the original response
            return jsonify({"message": resp})
    except configparser.Error as e:
        return jsonify({"error": f"Configuration error: {str(e)}"}), 500
    except Exception as e:
        return jsonify({"error": f"API call failed: {str(e)}"}), 500

def generate_prompt(disease_description: str) -> str:
    """Generate a prompt string by replacing the template placeholder with the given disease description.
    
    Args:
        disease_description: A non-empty string describing the disease
        
    Returns:
        The generated prompt string with the template placeholder replaced
        
    Raises:
        ValueError: If disease_description is empty
        FileNotFoundError: If prompt.txt is not found
        UnicodeError: If encoding/decoding fails
    """
    if not disease_description:
        raise ValueError("Disease description cannot be empty")
        
    # Get the path to prompt.txt in the same directory as this file
    prompt_path = os.path.join(os.path.dirname(__file__), 'prompt.txt')
    
    try:
        with open(prompt_path, 'r', encoding='utf-8') as file:
            template = file.read()
            
        # Ensure both template and description are properly encoded
        encoded_description = disease_description.encode('utf-8').decode('utf-8')
        return template.replace('{DISEASE_DESCRIPTION}', encoded_description)
    except FileNotFoundError:
        raise FileNotFoundError("prompt.txt not found in the project directory")
    except UnicodeError as e:
        raise UnicodeError(f"Encoding error: {str(e)}")

@app.route('/')
def index():
    """Render the main index page with chat interface"""
    return render_template('index.html')

def get_deepseek_config() -> dict:
    """Get DeepSeek API configuration from config file or environment variables.
    
    Returns:
        dict: Configuration dictionary with keys: base_url, api_key, model
        
    Raises:
        configparser.Error: If config file is invalid or API key is missing
    """
    # Default configuration
    config = {
        'base_url': 'https://api.deepseek.com',
        'api_key': None,
        'model': 'deepseek-chat'
    }
    
    # Try to read from config file if exists
    config_path = os.path.join(os.path.dirname(__file__), 'config.ini')
    if os.path.exists(config_path):
        parser = configparser.ConfigParser()
        parser.optionxform = str  # Preserve case but make section lookup case-insensitive
        parser.read(config_path, encoding='utf-8')
        
        # Find the deepseek section (case-insensitive)
        sections_lower = [s.lower() for s in parser.sections()]
        if 'deepseek' in sections_lower:
            section_name = parser.sections()[sections_lower.index('deepseek')]
            config.update({
                'base_url': parser[section_name].get('base_url', config['base_url']).rstrip('/'),
                'api_key': parser[section_name].get('api_key', config['api_key']),
                'model': parser[section_name].get('model', config['model'])
            })
    
    # Fallback to environment variable for API key
    config['api_key'] = config['api_key'] or os.getenv('DEEPSEEK_API_KEY')
    
    if not config['api_key']:
        raise configparser.Error(
            "API key is required. Please set it in config.ini or DEEPSEEK_API_KEY environment variable"
        )
    
    return config

def call_deepseek(prompt: str) -> str:
    """Call DeepSeek API to get response for the given prompt
    
    Args:
        prompt: The complete prompt string to send to DeepSeek
        
    Returns:
        The response content from DeepSeek API
        
    Raises:
        Exception: If API call fails or returns error
        configparser.Error: If config file is missing or invalid
    """
    # Get configuration
    config = get_deepseek_config()
    
    url = f"{config['base_url']}/chat/completions"
    headers = {
        "Authorization": f"Bearer {config['api_key']}",
        "Content-Type": "application/json"
    }
    data = {
        "model": config['model'],
        "messages": [
            {"role": "system", "content": "You are a helpful assistant"},
            {"role": "user", "content": prompt}
        ],
        "stream": False
    }
    
    response = requests.post(url, headers=headers, json=data)
    if response.status_code != 200:
        raise Exception(f"DeepSeek API request failed with status {response.status_code}")
    
    result = response.json()
    return result['choices'][0]['message']['content']

def run_app():
    """Run the Flask application on port 7777"""
    app.run(host='0.0.0.0', port=7777)

if __name__ == '__main__':
    run_app()